home *** CD-ROM | disk | FTP | other *** search
- // uncompresshelp.m
- // cc uncompresshelp.m -g -O -s -o uncompresshelp -lIndexing_s
- //
- // Copyright 1993, 1994, Scott Hess. This source code may be
- // redistributed and modified without restriction. Well, one
- // restriction - do not claim that you wrote it. If the user
- // receives non-trivial advantage from the use of this program, a
- // payment of $50-$100 would be appreciated.
- //
- // [I guess that would make this semi-shareware. I'm not much
- // interested in turning a profit (Ha!) on this program. Rather,
- // I am interested in getting some small return on time and energy
- // invested so that I can work on other, similar programs. You
- // know, little programs that aren't worth doing for their own
- // sake, but when you need them, you NEED them. I love poking
- // around in the obscure corners of NeXTSTEP, but too often I
- // can't spend much time doing it because it's hard to justify
- // the hours. It's like a hobby that brings in a small income.
- // You wouldn't want to live on it, because in reality the income
- // barely covers equipment and supplies, but it's useful as
- // justification when someone says "Why do you keep wasting your
- // time with that?" I also want to know if people find stuff like
- // this _useful_, or just interesting ...]
- //
- // Scott Hess
- // 12901 Upton Avenue South, #326
- // Burnsville, MN 55337
- //
- //
- // uncompresshelp [helpfile.store [outputdir]]
- //
- // uncompresshelp extracts help files from a help storefile and
- // stores them back onto the filesystem. helpfile.store is a help
- // storefile as generated by compresshelp. outputdir is the
- // directory to store the files in. helpfile.store defaults to
- // Help.store. outputdir defaults to the input file with .store
- // stripped from the end.
- //
- //
- // To muck about with the Help.store files:
- // o Open an IXStoreFile instance on the file.
- // o Within the store, open an IXBTree on block one, which contains
- // entries mapping the name of the helpfile element to the
- // files which are contained within it.
- // o Within the store, open an IXBTree on block two, which contains
- // entries mapping integer indices to file contents.
- // o The directory btree's values are of the form
- // "filename key;[filename key;...]". The filenames are files
- // that are contained within the helpfile element, while the
- // keys are integers which index into the file btree's to find
- // the contents of the file.
- // o Individual file content keys may appear more than once in
- // the directory. This means that the specified files are the
- // same [implemented as a call to link() in extractFile].
- // o File's which end in .rtf are compressed using /usr/ucb/compress.
- // So is .index.store, apparently.
- //
- //
- // Version history:
- //
- // Wednesday, January 12, 1994:
- // Fix inverted cur test in extractFile().
- //
- // Rewrite uncompress call in terms of raw fork/execl/wait4.
- // Previously it used system(), which did not work correctly
- // when the filename contained spaces. It also wouldn't have
- // worked if the filename contained shell metacharacters (which
- // was the reasoning behind not just putting quotes or
- // double-quotes in the system call).
- //
- // Added more descriptive and debugging output.
- //
- // Monday, January 10, 1994:
- // First public release.
- #import <libc.h>
- #import <indexing/indexing.h>
- #import <objc/HashTable.h>
-
- static HashTable *extractedFiles=nil;
- int uncompress( const char *filename)
- {
- int pid;
- if( !(pid=fork())) {
- execl( "/usr/ucb/uncompress", "/usr/ucb/uncompress", filename, NULL);
- exit( 1);
- } else if( pid==-1) {
- perror( "uncompresshelp: uncompress error");
- return -1;
- } else {
- union wait status;
- int ret=wait4( pid, &status, 0, NULL);
- if( ret==-1) {
- perror( "uncompresshelp: waiting for uncompress");
- return -1;
- } else if( WIFSIGNALED( status)) {
- fprintf( stderr, "uncompresshelp: compress terminated on signal %d.\n", status.w_termsig);
- return -1;
- } else if( status.w_retcode) {
- fprintf( stderr, "uncompresshelp: compress exited with %d.\n", status.w_retcode);
- return -1;
- }
- }
- return 0;
- }
- BOOL extractFile( IXBTreeCursor *fileCursor, unsigned fileId, const char *target)
- {
- // If we've already extracted that index, just link the
- // target to it.
- const char *cur=[extractedFiles valueForKey:(void *)fileId];
- if( cur) {
- fprintf( stderr, "\tLinking to %s\n", cur);
- if( link( cur, target)==-1) {
- perror( "uncompresshelp: linking to extracted file");
- }
-
- // Otherwise, we have to reach into the btree using fileCursor
- // and find it.
- } else {
- void *file=NULL;
- unsigned fileLen;
-
- // Make certain we have a table to map extracted files.
- if( !extractedFiles) {
- extractedFiles=[[HashTable alloc] initKeyDesc:"i" valueDesc:"%"];
- }
-
- // Store the mapping for this index.
- [extractedFiles insertKey:(void *)fileId value:(void *)NXUniqueString( target)];
-
- // Try to find the index in the table.
- if( [fileCursor setKey:(void *)(&fileId) andLength:sizeof( fileId)]) {
- int fd;
- const char *slash, *dot;
-
- // Read the contents of the file.
- fileLen=[fileCursor readValue:&file];
-
- // Open the file and stuff the contents into it.
- fd=open( target, O_WRONLY|O_CREAT, 0644);
- if( fd!=-1) {
- if( write( fd, file, fileLen)<fileLen) {
- perror( "uncompresshelp: Writing file");
- }
- if( close( fd)==-1) {
- perror( "uncompresshelp: Closing file");
- }
- } else {
- perror( "uncompresshelp: Opening file");
- }
-
- // Free the contents.
- free( file);
-
- // If the file is .rtf, uncompress it.
- slash=rindex( target, '/');
- slash=(slash ? slash+1 : target);
- dot=rindex( slash, '.');
- if( dot) {
- if( !strcmp( dot, ".rtf") || !strcmp( slash, ".index.store")) {
- char targetZ[ MAXPATHLEN+1];
- sprintf( targetZ, "%s.Z", target);
- fprintf( stderr, "\tRenaming %s\n\t to %s\n", target, targetZ);
- rename( target, targetZ);
- fprintf( stderr, "\tUncompressing %s\n", targetZ);
- uncompress( targetZ);
- }
- }
- }
- }
- }
- // Create the directory, creating parent directories as needed.
- int mkdirs( char *dir, int mode)
- {
- if( access( dir, F_OK)) {
- int ret=0;
- char *slash=rindex( dir, '/');
- if( slash) {
- *slash='\0';
- ret=mkdirs( dir, mode);
- *slash='/';
- }
- if( !ret) {
- ret=mkdir( dir, mode);
- }
- return ret;
- }
- return 0;
- }
- // *files is a file list in the format "filename key;[ filename
- // key;...]". Parse out the first filename/key pair into
- // outputp/*indexp, and put the end position back into *files.
- // Return YES if a filename was found. This routine is more
- // complex than needed because I was concerned that my only
- // knowledge of what's possible for filenames is empirical -
- // I know what's in some sample files, but I don't know whether
- // there are other possibilities (consider spaces in filenames,
- // for instance). Better safe than sorry.
- BOOL getNextFile( const char **files, char *outputp, unsigned *indexp)
- {
- const char *s, *p;
- for( s=*files; *s; s++) {
- // Get to a space followed by a digit.
- if( *s==' ' && isdigit( s[ 1])) {
- // Start accumulating the index.
- unsigned i=s[ 1]-'0';
- // Store away the end of the name.
- p=s;
- // Accumulate the rest of the index.
- for( s+=2; isdigit( *s); s++) {
- i*=10;
- i+=s[ 0]-'0';
- }
- // It _must_ end with ; to be valid.
- if( *s==';') {
- // Store away the file name.
- strncpy( outputp, *files, p-(*files));
- outputp[ p-(*files)]='\0';
- // Store away the index found.
- *indexp=i;
- // Return the new search position, skipping
- // the extra space after interior elements of
- // the list.
- *files=(s[ 1] ? s+2 : s+1);
- return YES;
- }
- }
- }
- return NO;
- }
- void main( int argc, char **argv)
- {
- const char *inputFile="Help.store";
- char outputDir[ MAXPATHLEN+1]="Help";
- unsigned outputDirLen;
- IXStoreFile *helpStore;
- IXBTree *dirTree, *fileTree;
- IXBTreeCursor *dirCursor, *fileCursor;
-
- // Decode command-line parameters.
- if( argc>1) {
- inputFile=argv[ 1];
- if( argc>2) {
- strcpy( outputDir, argv[ 2]);
- } else {
- const char *dot=rindex( inputFile, '.');
- if( dot && !strcmp( dot, ".store")) {
- strncpy( outputDir, inputFile, dot-inputFile);
- outputDir[ dot-inputFile]='\0';
- } else {
- fprintf( stderr, "uncompresshelp:\tUnable to generate default outputDir:\n\t\t%s doesn't end in .store.\n", inputFile);
- exit( 1);
- }
- }
- }
- outputDirLen=strlen( outputDir);
- outputDir[ outputDirLen++]='/';
- fprintf( stderr, "Extracting %s into %s\n", inputFile, outputDir);
-
- // Open up the storefile.
- helpStore=[[IXStoreFile alloc] initFromFile:inputFile forWriting:NO];
- if( !helpStore) {
- fprintf( stderr, "Couldn't open %s\n", inputFile);
- exit( 1);
- }
-
- // Open up a tree to list our directories and files in
- // them.
- dirTree=[[IXBTree alloc] initFromBlock:1 inStore:helpStore];
- dirCursor=[[IXBTreeCursor alloc] initWithBTree:dirTree];
-
- // Open up a tree to map indices to files.
- fileTree=[[IXBTree alloc] initFromBlock:2 inStore:helpStore];
- fileCursor=[[IXBTreeCursor alloc] initWithBTree:fileTree];
-
- // Start from the first entry in the directory list.
- if( [dirCursor setFirst]) {
- do {
- char *dir=NULL, *files=NULL;
- const char *s;
- unsigned dirLen, filesLen;
-
- // Get the directory entry.
- if( [dirCursor getKey:(void **)&dir andLength:&dirLen]) {
- unsigned i;
- const char *s;
-
- // Append the entry to the target directory.
- strncpy( outputDir+outputDirLen, dir, dirLen);
- outputDir[ outputDirLen+dirLen-1]='\0';
-
- // Read the list of files for that entry.
- filesLen=[dirCursor readValue:(void **)&files];
-
- // Look for the first file in the list.
- s=files;
- if( getNextFile( &s, outputDir+outputDirLen+dirLen, &i)) {
- // If that's the end, check to see if the
- // directory entry's basename matches the
- // only entry in the directory.
- if( !*s) {
- char *b=rindex( outputDir, '/');
- if( b && !strcmp( b+1, outputDir+outputDirLen+dirLen)) {
- // If they match, then extract directly
- // into the name specified by the
- // directory entry.
- fprintf( stderr, "Extracting simple file %s\n", outputDir);
- *b='\0';
- mkdirs( outputDir, 0755);
- *b='/';
- extractFile( fileCursor, i, outputDir);
- // And go to the next entry.
- continue;
- }
- }
-
- // Make certain the directory(s) for the
- // entry exist.
- mkdirs( outputDir, 0755);
- fprintf( stderr, "Extracting compound file %s\n", outputDir);
-
- // Put a slash between the parent dir and
- // the filenames within it.
- outputDir[ outputDirLen+dirLen-1]='/';
-
- // Extract each of the contained files.
- do {
- fprintf( stderr, " Extracting %s\n", outputDir);
- extractFile( fileCursor, i, outputDir);
- } while( getNextFile( &s, outputDir+outputDirLen+dirLen, &i));
- }
- free( files);
- }
- } while( [dirCursor setNext]);
- }
- }
-